Week 14 - STEM 691

Interactive maps with R

Joshua Rosenberg and Isabella Velásquez

Agenda

  • Final projects
  • Interactive maps
  • Revisit static maps
  • Interactive maps with plotly
  • Interactive maps with leaflet
  • Publishing interactive maps

Final projects

Rubric

Given a whole semesters’ worth of data viz know-how, how would you assess a data visualization? Which principles and practices would you want to see employed?

We’ve come up with a starting point:

https://bit.ly/stem691-rubric

Small group discussion

In small groups in a breakout room, please discuss 3-5 things that you would like to either change or modify. You can make suggestions related to the Major Categories, Expectations, or Point Value.

Discuss for five minutes and return to the whole group with a plan for how to share.

Process

For next week, you will need to develop up to three slides. Here are the requirements:

  • Presenters should discuss the story of the data, using the visualizations as aids.
  • Your presentation should be no more than 3 slides
  • Your presentation should take 3 minutes or fewer
  • Presenters should only speak for 1-2 minutes and take time to answer any questions or interact with the other attendees who appear in their booth.
  • This presentation style mimics actual poster presentations that occur at many conferences.

Process

You can use Google Slides, Powerpoint, Keynote, or another tool.

To ensure you receive full credit, please upload or share a link to your presentation by 9:00 am on Wednesday, May 1 for the Final Project assignment:

https://utk.instructure.com/courses/196893/assignments/1779345

Interactive maps in R

Interactivity

  • New York Times graphics editor Gregor Aisch noted that only 10 to 15 percent of readers who visit an interactive visualization on their site actually click on anything.
  • Is it worth the time and effort to make these things?

As with most design-related things, it depends on the goals and the audience of your visualization.

Interactivity

  • Immersion
  • Flexibility
  • Zooming, hover-over, pop-ups

Packages for interactive maps in R

  • plotly + ggplot
  • leaflet

Other packages include:

  • ggigraph + ggplot
  • highcharter

Revisit static maps

Species Occurrence data

The rgbif package accesses data from the Global Biodiversity Information Facility (GBIF).

library(tidyverse)
library(rgbif)

Species Occurrence data

Let’s say we want to download data on the Monarch butterfly.

dan_ple <- rgbif::occ_data(
  scientificName = 'Danaus plexippus'
)

dan_ple_dat <- dan_ple$data
glimpse(dan_ple_dat)
Rows: 500
Columns: 83
$ key                           <chr> "4507686934", "4507704024", "4507984043"…
$ scientificName                <chr> "Danaus plexippus (Linnaeus, 1758)", "Da…
$ decimalLatitude               <dbl> -34.90042, -34.86286, -26.45418, 28.4724…
$ decimalLongitude              <dbl> 138.69976, 139.39130, 152.63339, -16.253…
$ issues                        <chr> "cdc,cdround", "cdc,cdround", "cdc,cdrou…
$ datasetKey                    <chr> "50c9509d-22c7-4a22-a47d-8c48425ef4a7", …
$ publishingOrgKey              <chr> "28eb1a3f-1c15-4a95-931a-4af90ecb574d", …
$ installationKey               <chr> "997448a8-f762-11e1-a439-00145eb45e9a", …
$ hostingOrganizationKey        <chr> "28eb1a3f-1c15-4a95-931a-4af90ecb574d", …
$ publishingCountry             <chr> "AU", "AU", "AU", "US", "US", "US", "US"…
$ protocol                      <chr> "DWC_ARCHIVE", "DWC_ARCHIVE", "DWC_ARCHI…
$ lastCrawled                   <chr> "2024-04-21T08:18:21.750+00:00", "2024-0…
$ lastParsed                    <chr> "2024-04-21T17:35:12.192+00:00", "2024-0…
$ crawlId                       <int> 451, 451, 451, 451, 451, 451, 451, 451, …
$ basisOfRecord                 <chr> "HUMAN_OBSERVATION", "HUMAN_OBSERVATION"…
$ occurrenceStatus              <chr> "PRESENT", "PRESENT", "PRESENT", "PRESEN…
$ lifeStage                     <chr> "Larva", "Adult", "Larva", "Adult", "Adu…
$ taxonKey                      <int> 5133088, 5133088, 5133088, 5133088, 5133…
$ kingdomKey                    <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ phylumKey                     <int> 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, …
$ classKey                      <int> 216, 216, 216, 216, 216, 216, 216, 216, …
$ orderKey                      <int> 797, 797, 797, 797, 797, 797, 797, 797, …
$ familyKey                     <int> 7017, 7017, 7017, 7017, 7017, 7017, 7017…
$ genusKey                      <int> 5133087, 5133087, 5133087, 5133087, 5133…
$ speciesKey                    <int> 5133088, 5133088, 5133088, 5133088, 5133…
$ acceptedTaxonKey              <int> 5133088, 5133088, 5133088, 5133088, 5133…
$ acceptedScientificName        <chr> "Danaus plexippus (Linnaeus, 1758)", "Da…
$ kingdom                       <chr> "Animalia", "Animalia", "Animalia", "Ani…
$ phylum                        <chr> "Arthropoda", "Arthropoda", "Arthropoda"…
$ order                         <chr> "Lepidoptera", "Lepidoptera", "Lepidopte…
$ family                        <chr> "Nymphalidae", "Nymphalidae", "Nymphalid…
$ genus                         <chr> "Danaus", "Danaus", "Danaus", "Danaus", …
$ species                       <chr> "Danaus plexippus", "Danaus plexippus", …
$ genericName                   <chr> "Danaus", "Danaus", "Danaus", "Danaus", …
$ specificEpithet               <chr> "plexippus", "plexippus", "plexippus", "…
$ taxonRank                     <chr> "SPECIES", "SPECIES", "SPECIES", "SPECIE…
$ taxonomicStatus               <chr> "ACCEPTED", "ACCEPTED", "ACCEPTED", "ACC…
$ iucnRedListCategory           <chr> "LC", "LC", "LC", "LC", "LC", "LC", "LC"…
$ dateIdentified                <chr> "2024-01-01T01:12:40", "2024-01-01T04:19…
$ coordinateUncertaintyInMeters <dbl> 7, 3, 29845, 4, 5, NA, 11, NA, NA, NA, 4…
$ continent                     <chr> "OCEANIA", "OCEANIA", "OCEANIA", "AFRICA…
$ stateProvince                 <chr> "South Australia", "South Australia", "Q…
$ year                          <int> 2024, 2024, 2024, 2024, 2024, 2024, 2024…
$ month                         <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ day                           <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ eventDate                     <chr> "2024-01-01T11:41:07", "2024-01-01T11:11…
$ startDayOfYear                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ endDayOfYear                  <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ modified                      <chr> "2024-01-01T03:35:31.000+00:00", "2024-0…
$ lastInterpreted               <chr> "2024-04-21T17:35:12.192+00:00", "2024-0…
$ references                    <chr> "https://www.inaturalist.org/observation…
$ license                       <chr> "http://creativecommons.org/licenses/by-…
$ isSequenced                   <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE…
$ isInCluster                   <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE…
$ datasetName                   <chr> "iNaturalist research-grade observations…
$ recordedBy                    <chr> "Graham Armstrong", "sarcastrophe", "Jac…
$ identifiedBy                  <chr> "Graham Armstrong", "sarcastrophe", "Jac…
$ geodeticDatum                 <chr> "WGS84", "WGS84", "WGS84", "WGS84", "WGS…
$ class                         <chr> "Insecta", "Insecta", "Insecta", "Insect…
$ countryCode                   <chr> "AU", "AU", "AU", "ES", "US", "US", "US"…
$ gbifRegion                    <chr> "OCEANIA", "OCEANIA", "OCEANIA", "EUROPE…
$ country                       <chr> "Australia", "Australia", "Australia", "…
$ publishedByGbifRegion         <chr> "OCEANIA", "OCEANIA", "OCEANIA", "NORTH_…
$ rightsHolder                  <chr> "Graham Armstrong", "sarcastrophe", "Jac…
$ identifier                    <chr> "195422818", "195435992", "195447354", "…
$ `http://unknown.org/nick`     <chr> "grahamarmstrong", "sarcastrophe", "arch…
$ verbatimEventDate             <chr> "2024-01-01 11:41:07", "2024-01-01 11:11…
$ verbatimLocality              <chr> "Woodforde SA 5072, Australia", "Pellari…
$ collectionCode                <chr> "Observations", "Observations", "Observa…
$ gbifID                        <chr> "4507686934", "4507704024", "4507984043"…
$ occurrenceID                  <chr> "https://www.inaturalist.org/observation…
$ taxonID                       <chr> "48662", "48662", "48662", "48662", "486…
$ catalogNumber                 <chr> "195422818", "195435992", "195447354", "…
$ institutionCode               <chr> "iNaturalist", "iNaturalist", "iNaturali…
$ eventTime                     <chr> "11:41:07+10:30", "11:11:32+10:30", "16:…
$ `http://unknown.org/captive`  <chr> "wild", "wild", "wild", "wild", "wild", …
$ identificationID              <chr> "439486498", "439524139", "439552350", "…
$ informationWithheld           <chr> NA, NA, "Coordinate uncertainty increase…
$ occurrenceRemarks             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ infraspecificEpithet          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ sex                           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ distanceFromCentroidInMeters  <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ name                          <chr> "Danaus plexippus (Linnaeus, 1758)", "Da…

Download the world map data from rnaturalearth

The package rnaturalearth provides a map of countries of the entire world.

  • Use ne_countries to pull country data and choose the scale (rnaturalearthhires is necessary for scale = “large”)
  • Recall the simple feature (sf) standard of data
# install.packages("rnaturalearth")
# install.packages("rnaturalearthdata")
library(rnaturalearth)

world <- ne_countries(scale = "medium", returnclass = "sf")
class(world)
[1] "sf"         "data.frame"

Making a map

First, let’s start with creating a base map of the world using ggplot2’s geom_sf().

library(ggplot2)
theme_set(theme_minimal())

ggplot(data = world) +
    geom_sf()

Making a map

Reminder that sf geometries are no different than regular geometries, and can be displayed with the same level of control on their attributes:

ggplot(data = world) + 
    geom_sf(color = "white", 
            linewidth = 0.1,
            fill = "#0C1337")

Making a map

We can layer the Monarch butterfly data from dan_ple_dat:

  • Add a new points geometry with geom_point()
  • Specify the data (since it differs from world) with data
  • Map longitude/latitude to the data with aes()

Making a map

ggplot(data = world) + 
    geom_sf(color = "white", 
            linewidth = 0.1,
            fill = "#0C1337") +
  geom_point(data = dan_ple_dat,
             aes(x = decimalLongitude,
                 y = decimalLatitude))

Making a static map

We can layer edit the points according to life stage (lifeStage) by specifying color in aes().

Note

Anything not mapped to the data can be put outside of aes()

Making a static map

ggplot(data = world) + 
    geom_sf(color = "white",
            linewidth = 0.1,
            fill = "#0C1337") +
  geom_point(data = dan_ple_dat,
             aes(x = decimalLongitude,
                 y = decimalLatitude,
                 color = lifeStage),
             size = 0.5)

Adding interactivity

ggplot2 + plotly

plotly is an R package for creating interactive web-based graphs via the open source JavaScript graphing library plotly.js.

# install.packages("plotly")
library(plotly)

ggplot2 + plotly

Use the function ggplotly() to draw the graph with plotly.js.

  • Printing the Plotly object will render the chart locally in your web browser or in the RStudio viewer

ggplot2 + plotly

Wrap your plot in ggplotly():

p <- ggplot(data = world) +
  geom_sf(color = "white",
          linewidth = 0.1,
          fill = "#0C1337") +
  geom_point(
    data = dan_ple_dat,
    aes(x = decimalLongitude,
        y = decimalLatitude,
        color = lifeStage),
    size = 0.5
  )

ggplotly(p)

ggplot2 + plotly

Code along

Leaftlet

Leaflet is an open-source JavaScript library for interactive maps.

  • Simplicity, performance, and usability
  • Lots of plugins and documentation

The leaflet package

Leaflet is a JavaScript library used to build web mapping applications. The leaflet R package makes it easy to create Leaflet maps from R.

# install.packages("leaflet")
library(leaflet)

The leaflet package: base map

Create a base map with addTiles().

leaflet() %>% addTiles()

The leaflet package: tiles

Specify different tile options with addProviderTiles

leaflet() %>% addProviderTiles(providers$Esri.NatGeoWorldMa)

The leaflet package: zoom

  • The different components of the map can be added using the pipe operator %>%.

The leaflet package: zoom

library(leaflet)
library(dplyr)

leaflet() %>% 
  addProviderTiles(providers$Esri.NatGeoWorldMa) %>% 
  setView(lng = -83.9294, lat = 35.9546, zoom = 14)

The leaflet package: markers and tooltips

We can do things like add markers and tooltips.

The leaflet package: markers and tooltips

leaflet() %>%
  addProviderTiles(providers$Esri.NatGeoWorldMa) %>%
  setView(lng = -83.9294, lat = 35.9546, zoom = 14) %>%
  addMarkers(lng = -83.9294, lat = 35.9546,
             popup = "<a href='https://www.utk.edu/'>University of Tennessee, Knoxville</a>")

The leaflet package: bringing in data

leaflet(dan_ple_dat) %>%
  addProviderTiles(providers$Esri.NatGeoWorldMa) %>%
  setView(lng = -118.259,
          lat = 34.0507666,
          zoom = 6) %>% 
  addCircles(
    ~ decimalLongitude,
    ~ decimalLatitude,
    popup = dan_ple_dat$scientificName
  ) 

The leaflet package: bringing in data

The leaflet package: customizing maps

leaflet(dan_ple_dat) %>%
  addProviderTiles(providers$Esri.NatGeoWorldMa) %>%
  setView(lng = -118.259,
          lat = 34.0507666,
          zoom = 6) %>% 
  addCircles(
    ~ decimalLongitude,
    ~ decimalLatitude,
    popup = dan_ple_dat$scientificName,
    weight = 9,
    radius = 40,
    color = "#ffa500",
    stroke = TRUE,
    fillOpacity = 0.8
  ) 

The leaflet package: customizing maps

The leaflet package: adding polygons

leaflet(data = world) %>%
  addTiles() %>%
  addPolygons(
    fillColor = topo.colors(10, alpha = NULL),
    stroke = FALSE,
    label = ~ name
  )

The leaflet package: adding polygons

The leaflet package: making a choropleth

world %>% 
  select(name, gdp_md) %>% 
  head()
Simple feature collection with 6 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -73.36621 ymin: -22.40205 xmax: 109.4449 ymax: 41.9062
Geodetic CRS:  WGS 84
       name gdp_md                       geometry
1  Zimbabwe  21440 MULTIPOLYGON (((31.28789 -2...
2    Zambia  23309 MULTIPOLYGON (((30.39609 -1...
3     Yemen  22581 MULTIPOLYGON (((53.08564 16...
4   Vietnam 261921 MULTIPOLYGON (((104.064 10....
5 Venezuela 482359 MULTIPOLYGON (((-60.82119 9...
6   Vatican    -99 MULTIPOLYGON (((12.43916 41...

The leaflet package: making a choropleth

Specify a color palette and a domain:

pal <- colorNumeric("YlOrRd",
                    domain = world$gdp_md)

The leaflet package: making a choropleth

leaflet(data = world) %>%
  addTiles() %>%
  setView(lng = -118.259,
          lat = 34.0507666,
          zoom = 2) %>% 
  addPolygons(
    fillColor = ~ pal(gdp_md),
    stroke = FALSE
  )

The leaflet package: making a choropleth

The leaflet package: add labels

labels <- paste0(
  "<strong>", world$name, "</strong><br/>GDP: ", world$gdp_md) %>% lapply(htmltools::HTML)

leaflet(data = world) %>%
  addTiles() %>%
  setView(lng = -118.259,
          lat = 34.0507666,
          zoom = 2) %>% 
  addPolygons(
    fillColor = ~ pal(gdp_md),
    stroke = FALSE,
    label = labels
  )

The leaflet package: add labels

Code along

What’s next?

Assignment 12 will walk you through creating a Leaflet map.

  • Please reach out with any issues!

Final projects!